/*
 * REFXMLDtdParser.h
 *
 *  Created on: Apr 1, 2013
 *      Author: Lucifer
 */

#ifndef	__BFX_REFXMLDTDPARSER_H__
#define	__BFX_REFXMLDTDPARSER_H__

#include "REFXMLSchemaInfo.h"
#include "../core/REFNameTable.h"
#include "../core/XMLLineInfo.h"
#include "../../unicode/REFUniStringBuilder.h"

namespace BFX
{
//////////////////////////////////////////////////////////////////////////////
class REFXMLDtdParserAdapter: public REFObject
{
public:
	//
	// Basic methods
	//

	// Gets the current xml name table.
	virtual REFNameTable* GetNameTable() const = 0;

	// Gets the current parsing buffer.
	virtual UCHAR* GetParsingBuffer() const = 0;
	// Gets the size of the current parsing buffer.
	virtual int GetParsingBufferLength() const = 0;
	// Gets the offset of the current parsing buffer.
	virtual int GetCurrentPosition() const = 0;
	// Sets the offset of the current parsing buffer.
	virtual void SetCurrentPosition(int nPos) = 0;
	// Gets the current line number.
	virtual int GetLineNumber() const = 0;
	// Gets the start position of the current line.
	virtual int GetLineStartPosition() const = 0;
	// Determine whether end of file reached.
	virtual bool IsEof() const = 0;
	// Gets The base URL of current xml document.
	virtual UniString GetBaseURI() const = 0;
	// Gets the entity stack size.
	virtual int GetEntityStackLength() const = 0;

	//
	// External (XML common syntax) parsing methods.
	//
	virtual int ParseNumericCharRef(REFUniStringBuilder* sb) = 0;
	virtual int ParseNamedCharRef(bool bExpand, REFUniStringBuilder* sb) = 0;
	virtual bool ParsePI(REFUniStringBuilder* sb) = 0;
	virtual bool ParseComment(REFUniStringBuilder* sb) = 0;

	virtual bool PushEntity(REFXMLSchemaEntity* pEntity, int nEntityId) = 0;
	virtual bool PopEntity(OUT REFXMLSchemaEntity*& pOldEntity, OUT int& nNewEntityId) = 0;
	virtual bool PushExternalSubset(const UniString& strSystemId, const UniString& strPublicId) = 0;

	//
	// Misc methods.
	//
	virtual int ReadData() = 0;
	virtual void OnNewLine(int nPos) = 0;
	virtual void OnSystemId(const UniString& strSystemId, XMLLineInfo keywordLineInfo,
			XMLLineInfo systemLiteralLineInfo) = 0;
	virtual void OnPublicId(const UniString& strPublicId, XMLLineInfo keywordLineInfo,
			XMLLineInfo publicLiteralLineInfo) = 0;

	//
	// Error handing.
	//
	virtual XMLErrorContext GetLastError() const = 0;
	virtual void SetLastError(XMLError nErrorCode) = 0;
	virtual void SetLastError(XMLError nErrorCode, const String& strErrorMsg) = 0;
	virtual void SetLastError(XMLError nErrorCode, int nPos) = 0;
	virtual void SetLastError(XMLError nErrorCode, const String& strErrorMsg, int nPos) = 0;
	virtual void SetLastError(XMLError nErrorCode, int nLineNo, int nLinePos) = 0;
	virtual void SetLastError(XMLError nErrorCode, const String& strErrorMsg, int nLineNo, int nLinePos) = 0;
};

//////////////////////////////////////////////////////////////////////////////

class REFXMLDtdParser : public REFObject
{
private:
	//
	// Private types
	//
	enum Token
	{
		TokenError		= -1,	// error occurs when parseing token, you need check the error code.
		TokenCDATA		= 0,
		TokenID			= 1,
		TokenIDREF		= 2,
		TokenIDREFS		= 3,
		TokenENTITY		= 4,
		TokenENTITIES	= 5,
		TokenNMTOKEN	= 6,
		TokenNMTOKENS	= 7,
		TokenNOTATION	= 8,
		TokenNone,
		TokenPERef,
		TokenAttlistDecl,
		TokenElementDecl,
		TokenEntityDecl,
		TokenNotationDecl,
		TokenComment,
		TokenPI,
		TokenCondSectionStart,
		TokenCondSectionEnd,
		TokenEof,
		TokenREQUIRED,
		TokenIMPLIED,
		TokenFIXED,
		TokenQName,
		TokenName,
		TokenNmtoken,
		TokenQuote,
		TokenLeftParen,
		TokenRightParen,
		TokenGreaterThan,
		TokenOr,
		TokenLeftBracket,
		TokenRightBracket,
		TokenPUBLIC,
		TokenSYSTEM,
		TokenLiteral,
		TokenDOCTYPE,
		TokenNData,
		TokenPercent,
		TokenStar,
		TokenQMark,
		TokenPlus,
		TokenPCDATA,
		TokenComma,
		TokenANY,
		TokenEMPTY,
		TokenIGNORE,
		TokenINCLUDE,
	};

	enum ScanningFunction
	{
		ScanningFunctionSubsetContent,
		ScanningFunctionName,
		ScanningFunctionQName,
		ScanningFunctionNmtoken,
		ScanningFunctionDoctype1,
		ScanningFunctionDoctype2,
		ScanningFunctionElement1,
		ScanningFunctionElement2,
		ScanningFunctionElement3,
		ScanningFunctionElement4,
		ScanningFunctionElement5,
		ScanningFunctionElement6,
		ScanningFunctionElement7,
		ScanningFunctionAttlist1,
		ScanningFunctionAttlist2,
		ScanningFunctionAttlist3,
		ScanningFunctionAttlist4,
		ScanningFunctionAttlist5,
		ScanningFunctionAttlist6,
		ScanningFunctionAttlist7,
		ScanningFunctionEntity1,
		ScanningFunctionEntity2,
		ScanningFunctionEntity3,
		ScanningFunctionNotation1,
		ScanningFunctionCondSection1,
		ScanningFunctionCondSection2,
		ScanningFunctionCondSection3,
		ScanningFunctionLiteral,
		ScanningFunctionSystemId,
		ScanningFunctionPublicId1,
		ScanningFunctionPublicId2,
		ScanningFunctionClosingTag,
		ScanningFunctionParamEntitySpace,
		ScanningFunctionNone,
	};

	enum LiteralType
	{ 
		LiteralTypeAttributeValue,
		LiteralTypeEntityReplText,
		LiteralTypeSystemOrPublicID,
	};

	// Helper class to save information about undeclared notations.
	class UndeclaredNotation : public REFObject
	{
	public:
		UniString	m_strName;
		int			m_nLineNo;
		int			m_nLinePos;
		REF<UndeclaredNotation>	m_pNext;

		UndeclaredNotation(UniString strName, int nLineNo, int nLinePos);
	};
	typedef REFWrapper<HashMap<UniString, REF<UndeclaredNotation> > > UndeclaredNotationMap;

	// Constructors
public:
	REFXMLDtdParser(REFXMLDtdParserAdapter* pReaderAdapter);
	virtual ~REFXMLDtdParser();

	// Implementations
	bool Parse(bool bSaveInternalSubset);

	REFXMLSchemaInfo* GetSchemaInfo() const;
	UniString GetInternalSubset() const;

	XMLErrorContext GetLastError() const;
	bool IsErrorOccurred() const;

private:
	void SetLastError(XMLError nErrorCode);
	void SetLastError(XMLError nErrorCode, const String& strErrorMsg);
	void SetLastError(XMLError nErrorCode, int nPos);
	void SetLastError(XMLError nErrorCode, const String& strErrorMsg, int nPos);
	void SetLastError(XMLError nErrorCode, int nLineNo, int nLinePos);
	void SetLastError(XMLError nErrorCode, const String& strErrorMsg, int nLineNo, int nLinePos);

	//
	// Parsing buffer maintainance methods
	//
	int ReadData();
	bool ReadDataInName();
	void LoadParsingBuffer();
	void SaveParsingBuffer();
	void SaveParsingBuffer(int nInternalSubsetValueEndPos);

	bool ParseFreeFloatingDtd();
	bool ParseInDocumentDtd(bool bSaveInternalSubset);
	bool ParseInternalSubset();
	bool ParseExternalSubset();
	bool ParseSubset();
	bool ParseAttlistDecl();
	bool ParseAttlistType(REFXMLSchemaAttDef* pAttrDef, REFXMLSchemaElementDecl* pElementDecl);
	bool ParseAttlistDefault(REFXMLSchemaAttDef* pAttrDef);
	bool ParseElementDecl();
	bool ParseEntityDecl();
	bool ParseNotationDecl();
	bool ParseComment();
	bool ParsePI();
	bool ParseCondSection();
	bool ParseElementOnlyContent(REFXMLParticleContentValidator* pcv, int nStartParenEntityId);
	bool ParseElementMixedContent(REFXMLParticleContentValidator* pcv, int nStartParenEntityId);
	bool ParseHowMany(REFXMLParticleContentValidator* pcv);

	bool ParseExternalId(Token idTokenType, Token declType, OUT UniString& strPublicId, OUT UniString& strSystemId);
	UniString ParseUnexpectedToken(int nStartPos);

	//
	// Private properties
	//
	bool IsSaveInternalSubsetValue();
    bool IsParsingInternalSubset();
	bool IsParsingTopLevelMarkup();

	//
	// Scanning methods - works directly with parsing buffer
	//
	Token GetToken(bool bNeedWhiteSpace);
	Token ScanNameExpected();
	Token ScanQNameExpected();
	Token ScanNmtokenExpected();
	bool ScanName();
	bool ScanQName();
	bool ScanQName(bool bIsQName);
	bool ScanNmtoken();
	Token ScanDoctype1();
	Token ScanDoctype2();
	Token ScanSubsetContent();
	Token ScanElement1();
	Token ScanElement2();
	Token ScanElement3();
	Token ScanElement4();
	Token ScanElement5();
	Token ScanElement6();
	Token ScanElement7();
	Token ScanAttlist1();
	Token ScanAttlist2();
	Token ScanAttlist3();
	Token ScanAttlist4();
	Token ScanAttlist5();
	Token ScanAttlist6();
	Token ScanAttlist7();
	Token ScanNotation1();
	Token ScanSystemId();
	Token ScanPublicId1();
	Token ScanPublicId2();
	Token ScanEntity1();
	Token ScanEntity2();
	Token ScanEntity3();
	Token ScanCondSection1();
	Token ScanCondSection2();
	Token ScanCondSection3();
	Token ScanLiteral(LiteralType literalType);
	Token ScanClosingTag();
	REFXMLQualifiedName* ScanEntityName();

	bool EatPublicKeyword();
	bool EatSystemKeyword();

	//
	// Helper methods and properties
	//
	bool IsAttributeValueType(Token token) const;
	int GetLineNo() const;
	int GetLinePos() const;
	UniString GetBaseURI() const;

	//
	// Scanned data getters
	//
	REFXMLQualifiedName* GetNameQualified(bool bCanHavePrefix);
	UniString GetNameString() const;
	UniString GetNmtokenString() const;
	UniString GetValue() const;
	UniString GetValueWithStrippedSpaces();

	//
	// Entity handling
	//
	bool HandleEntityReference(bool bParamEntity, bool bInLiteral, bool bInAttribute);
	bool HandleEntityReference(REFXMLQualifiedName* pEntityName, bool bParamEntity, bool bInLiteral,
			bool bInAttribute);
	REFXMLSchemaEntity* VerifyEntityReference(REFXMLQualifiedName* pEntityName, bool bParamEntity,
			bool bMustBeDeclared, bool bInAttribute);
	bool HandleEntityEnd(bool bInLiteral);

private:
	// connector to reader
	REFXMLDtdParserAdapter*	m_pReaderAdapter;

	// name table
	REFNameTable*			m_pNameTable;
	// final schema info
	REF<REFXMLSchemaInfo>	m_pSchemaInfo;

	// system & public id
	UniString	m_strSystemId;
	UniString	m_strPublicId;

	bool		m_bNormalize;
	bool		m_bSupportNamespaces;
	bool		m_bV1Compat;
	bool		m_bValidate;

	// cached character buffer
	UCHAR*		m_pChars;
	int			m_nCharsUsed;
	int			m_nCurPos;

	// scanning function for the next token
	ScanningFunction		m_scanningFunction;
	ScanningFunction		m_nextScaningFunction;
	ScanningFunction		m_savedScanningFunction;	// this one is used only for adding spaces around parameter entities

	// flag if whitespace before token
	bool		m_bWhitespaceBeforeToken;

	// start position of the last token (for name and value)
	int			m_nTokenStartPos;

	// colon position (for name)
	int			m_nColonPos;

	// entities
	int			m_nExternalEntitiesDepth;
	int			m_nCurEntityId;
	int			m_nNextEntityId;

	// free-floating DTD support
	bool		m_bFreeFloatingDtd;
	bool		m_bHasFreeFloatingInternalSubset;

	// value of the internal subset
	REF<REFUniStringBuilder>	m_pInternalSubsetValueSb;
	UniString	m_strInternalSubsetValue;

	// misc
	REF<REFUniStringBuilder>	m_pStringBuilder;
	REF<UndeclaredNotationMap>	m_pUndeclaredNotations;
	int			m_nCondSectionDepth;
	XMLLineInfo	m_literalLineInfo;
	UCHAR		m_literalQuoteChar;
	UniString	m_strDocumentBaseUri;
	UniString	m_strExternalDtdBaseUri;
};

//////////////////////////////////////////////////////////////////////////////
// REFXMLDtdParser Inline methods

inline REFXMLDtdParser::UndeclaredNotation::UndeclaredNotation(UniString strName, int nLineNo, int nLinePos)
	: m_strName(strName), m_nLineNo(nLineNo), m_nLinePos(nLinePos), m_pNext(NULL)
{
}

inline REFXMLSchemaInfo* REFXMLDtdParser::GetSchemaInfo() const
{
	return m_pSchemaInfo;
}
inline UniString REFXMLDtdParser::GetInternalSubset() const
{
	return m_strInternalSubsetValue;
}

inline XMLErrorContext REFXMLDtdParser::GetLastError() const
{
	return m_pReaderAdapter->GetLastError();
}
inline bool REFXMLDtdParser::IsErrorOccurred() const
{
	return (GetLastError().GetErrorCode() != XML_Success);
}
inline void REFXMLDtdParser::SetLastError(XMLError nErrorCode)
{
	m_pReaderAdapter->SetLastError(nErrorCode);
}
inline void REFXMLDtdParser::SetLastError(XMLError nErrorCode, const String& strErrorMsg)
{
	m_pReaderAdapter->SetLastError(nErrorCode, strErrorMsg);
}
inline void REFXMLDtdParser::SetLastError(XMLError nErrorCode, int nPos)
{
	m_pReaderAdapter->SetLastError(nErrorCode, nPos);
}
inline void REFXMLDtdParser::SetLastError(XMLError nErrorCode, const String& strErrorMsg, int nPos)
{
	m_pReaderAdapter->SetLastError(nErrorCode, strErrorMsg, nPos);
}
inline void REFXMLDtdParser::SetLastError(XMLError nErrorCode, int nLineNo, int nLinePos)
{
	m_pReaderAdapter->SetLastError(nErrorCode, nLineNo, nLinePos);
}
inline void REFXMLDtdParser::SetLastError(XMLError nErrorCode, const String& strErrorMsg, int nLineNo, int nLinePos)
{
	m_pReaderAdapter->SetLastError(nErrorCode, strErrorMsg, nLineNo, nLinePos);
}

inline void REFXMLDtdParser::LoadParsingBuffer()
{
	m_pChars = m_pReaderAdapter->GetParsingBuffer();
	m_nCharsUsed = m_pReaderAdapter->GetParsingBufferLength();
	m_nCurPos = m_pReaderAdapter->GetCurrentPosition();
}
inline void REFXMLDtdParser::SaveParsingBuffer()
{
	SaveParsingBuffer(m_nCurPos);
}
//
// Private properties
//
inline bool REFXMLDtdParser::IsSaveInternalSubsetValue()
{ 
	return (m_pReaderAdapter->GetEntityStackLength() == 0 && m_pInternalSubsetValueSb != NULL);
}
inline bool REFXMLDtdParser::IsParsingInternalSubset()
{
	return m_nExternalEntitiesDepth == 0;
}
inline bool REFXMLDtdParser::IsParsingTopLevelMarkup()
{
	return m_scanningFunction == ScanningFunctionSubsetContent
			|| (m_scanningFunction == ScanningFunctionParamEntitySpace
					&& m_savedScanningFunction == ScanningFunctionSubsetContent);
}

inline REFXMLDtdParser::Token REFXMLDtdParser::ScanNameExpected()
{
	ScanName();
	m_scanningFunction = m_nextScaningFunction;
	return TokenName;
}

inline REFXMLDtdParser::Token REFXMLDtdParser::ScanQNameExpected()
{
	if (!ScanQName())
		return TokenError;

	m_scanningFunction = m_nextScaningFunction;
	return TokenQName;
}
inline REFXMLDtdParser::Token REFXMLDtdParser::ScanNmtokenExpected()
{
	if (!ScanNmtoken())
		return TokenError;
	m_scanningFunction = m_nextScaningFunction;
	return TokenNmtoken;
}
inline bool REFXMLDtdParser::ScanName()
{
	return ScanQName(false);
}
inline bool REFXMLDtdParser::ScanQName()
{
	return ScanQName(m_bSupportNamespaces);
}

inline bool REFXMLDtdParser::ParseInternalSubset()
{
	BFX_ASSERT(IsParsingInternalSubset());
	return ParseSubset();
}

inline bool REFXMLDtdParser::ParseExternalSubset()
{
	BFX_ASSERT(m_nExternalEntitiesDepth == 0);

	// push external subset
	if (!m_pReaderAdapter->PushExternalSubset(m_strSystemId, m_strPublicId))
	{
		return true;	// or fasle???
	}
	m_strExternalDtdBaseUri = m_pReaderAdapter->GetBaseURI();

	m_nExternalEntitiesDepth ++;
	LoadParsingBuffer();
	return ParseSubset();
}

//
// Helper methods and properties
//
inline bool REFXMLDtdParser::IsAttributeValueType(Token token) const
{
	return (int)token >= (int)TokenCDATA && (int)token <= (int)TokenNOTATION;
}
inline int REFXMLDtdParser::GetLineNo() const
{
	return m_pReaderAdapter->GetLineNumber();
}
inline int REFXMLDtdParser::GetLinePos() const
{
	return m_nCurPos - m_pReaderAdapter->GetLineStartPosition();
}
inline UniString REFXMLDtdParser::GetBaseURI() const
{
	return m_pReaderAdapter->GetBaseURI();
}
inline UniString REFXMLDtdParser::GetNameString() const
{
	BFX_ASSERT(m_nCurPos - m_nTokenStartPos > 0);
	 return UniString(m_pChars + m_nTokenStartPos, m_nCurPos - m_nTokenStartPos);
}

inline UniString REFXMLDtdParser::GetNmtokenString() const
{
	return GetNameString();
}

inline UniString REFXMLDtdParser::GetValue() const
{
    return (m_pStringBuilder->GetLength() == 0) ? UniString(m_pChars + m_nTokenStartPos, m_nCurPos - m_nTokenStartPos - 1) :
		m_pStringBuilder->ToString();
}

//
// Entity handling
//
inline bool REFXMLDtdParser::HandleEntityReference(bool bParamEntity, bool bInLiteral, bool bInAttribute)
{
	BFX_ASSERT(m_pChars[m_nCurPos] == '&' || m_pChars[m_nCurPos] == '%');
	m_nCurPos++;
	return HandleEntityReference(ScanEntityName(), bParamEntity, bInLiteral, bInAttribute);
}

}	//	namespace BFX

#endif	//	__BFX_REFXMLDTDPARSER_H__
